package system;


import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import model.Course;
import model.Discipline;
import model.Student;
import dataStructs.BTree;
import dataStructs.HashTable;
import dataStructs.Node;
import exceptions.ExceptionFound;
import exceptions.ExceptionRelationLoad;
import operations.Join;
import operations.Relation;
import operations.Tuple;
import util.FileOperations;

public class QuerySystem {
		
	
	private static final String pathStudents = "tables/students.tab";
	private static final String pathCourses = "tables/courses.tab";
	private static final String pathDisciplines = "tables/disciplines.tab";
	private static final String pathInscritions = "tables/inscritions.tab";
	private static final String pathjoinStudentByCourse = "tables/joins/joinStudentByCourse.tab";
	private static final String pathjoinStudentByDiscipline = "tables/joins/joinStudentByDiscipline.tab";
	private static final String pathjoinInscritionsStudenByCourse = "tables/joins/joinInscritionsStudenByCourse.tab";
	private static final String pathjoinDisciplinesByStudent = "tables/joins/joinDisciplinesByStudent.tab";
	
	
	
	private Relation studentsRelation;
	private Relation coursesRelation;
	private Relation inscritionsRelation;
	private Relation disciplinesRelation;
	
	private BTree<String, Student> students;
	private HashTable<String, Course> courses;
	private BTree<String, Discipline> disciplines;

	public QuerySystem() throws ExceptionRelationLoad {
		loadAllRelations();
		
		loadStudentsFromRelation();
		loadCoursesFromRelation();
		loadDisciplinesFromRelation();
		//loadInscritionsFromRelation();
	}
	
	
	//private void loadInscritionsFromRelation() {

	
	//}

	private void loadDisciplinesFromRelation() {
		Discipline discipline;
		ArrayList<Tuple> tuples = disciplinesRelation.getRows();
		this.disciplines = new BTree<String, Discipline>();
		
		for (Tuple t: tuples) {
			discipline = new Discipline((int) Integer.valueOf(t.getFieldValue(0)), t.getFieldValue(1), (int) Integer.valueOf(t.getFieldValue(2)));
			this.disciplines.add(new Node<String, Discipline>(discipline.getName(), discipline));
		} 
	}

	private void loadStudentsFromRelation() {
		
		Student student;
		ArrayList<Tuple> tuples = studentsRelation.getRows();
		this.students = new BTree<String, Student>();
		
		for (Tuple t: tuples) {
			student = new Student((int) Integer.valueOf(t.getFieldValue(0)), t.getFieldValue(1), t.getFieldValue(2), (int)Integer.valueOf(t.getFieldValue(3)));
			this.students.add(new Node<String, Student>(student.getName(), student));
		}
	}

	private void loadCoursesFromRelation() {
		Course course;
		ArrayList<Tuple> tuples = coursesRelation.getRows();
		
		this.courses = new HashTable<String, Course>();
		
		for (Tuple t: tuples) {
			course = new Course((int) Integer.valueOf(t.getFieldValue(0)), t.getFieldValue(1));
			this.courses.add(new Node<String, Course>(course.getName(), course));
		}
	}


	private void loadAllRelations() throws ExceptionRelationLoad{
		this.studentsRelation = new Relation(4, pathStudents);
		
		if (!studentsRelation.open())
			throw new ExceptionRelationLoad("Students");
		
		this.coursesRelation = new Relation(2, pathCourses);
		
		if (!coursesRelation.open())
			throw new ExceptionRelationLoad("Courses");
		
		this.inscritionsRelation = new Relation(3, pathInscritions);
		
		if (!inscritionsRelation.open())
			throw new ExceptionRelationLoad("Inscritions");
		
		this.disciplinesRelation = new Relation(3, pathDisciplines);
				
		if (!disciplinesRelation.open())
			throw new ExceptionRelationLoad("Disciplines");
	}
	
	
	public Relation joinStudentsByCourse(String courseName) throws ExceptionFound{
		Relation relationStudentsByCourse;
		Join<String, Tuple> join;
		
		Course course;
		String courseNameId;
		
		//restore course
		course = courses.get(courseName);
		
		if (course == null)
			throw new ExceptionFound("Course", courseName);
		
		courseNameId = String.valueOf(course.getCourse_id());


		join = Join.getSelectedJoin(studentsRelation, 3, coursesRelation, 0, pathjoinStudentByCourse);
		join.open();
		relationStudentsByCourse = join.getRelation(courseNameId);
		join.close();
				
		return relationStudentsByCourse;
	}

		
	public Relation joinStudentsByDisciplnes(String disciplineName) throws ExceptionFound{
		
		Relation relationInscritionsStudentsByDisciplines;
		Relation relationInscritionsByStudents;
		Join<String, Tuple> join;
		
		Discipline discipline;
		Node<String, Discipline> nodeDiscipline;
		String disciplineNameId;
		
		//restore discipline
		nodeDiscipline = this.disciplines.get(disciplineName);
		
		if (nodeDiscipline == null)
			throw new ExceptionFound("Discipline", disciplineName);
		
		discipline = nodeDiscipline.getValue();
		
		disciplineNameId = String.valueOf(discipline.getDiscipline_id());

		
		join = Join.getSelectedJoin(inscritionsRelation, 0, studentsRelation, 0, pathjoinStudentByDiscipline);
		join.open();
		relationInscritionsByStudents = join.getRelation();
		join.close();
		
		join = Join.getSelectedJoin(relationInscritionsByStudents, 1, disciplinesRelation, 0, pathjoinStudentByDiscipline);
		join.open();
		relationInscritionsStudentsByDisciplines = join.getRelation(disciplineNameId);
		join.close();
				
		return relationInscritionsStudentsByDisciplines;
	}
		
	public Relation joinInscritionsByCourse(String courseName) throws ExceptionFound{

		Relation relationInscritionsByStudent;
		Relation relationInscritionsStudentsByCourse;
		Join<String, Tuple> join;
		
		Course course;
		String courseNameId;
		
		//restore course
		course = courses.get(courseName);
		
		if (course == null)
			throw new ExceptionFound("Course", courseName);

		
		courseNameId = String.valueOf(course.getCourse_id());

		
		join = Join.getSelectedJoin(inscritionsRelation, 0, studentsRelation, 0, pathjoinInscritionsStudenByCourse);
		join.open();
		relationInscritionsByStudent = join.getRelation();
		join.close();
		
		join = Join.getSelectedJoin(relationInscritionsByStudent, 5, coursesRelation, 0, pathjoinInscritionsStudenByCourse);
		join.open();
		relationInscritionsStudentsByCourse = join.getRelation(courseNameId);
		join.close();
				
		return relationInscritionsStudentsByCourse;

	}
	
	public int countInscritions(Relation inscritionsRelation){
		return inscritionsRelation.size();
	}
	
	public Relation joinDisciplinesByStudent(String studentName) throws ExceptionFound{
		
		Relation relationInscritionsByDiscipline;
		Relation relationInscritionsDisciplinesByStudent;
		Join<String, Tuple> join;
		
		Node<String, Student> studentNode;
		Student student;
		String studentNameId;
		
		//restore student
		studentNode = students.get(studentName);
		
		if (studentNode == null)
			throw new ExceptionFound("Student", studentName);

		
		student = studentNode.getValue();
		studentNameId = String.valueOf(student.getStudent_id());

		
		join = Join.getSelectedJoin(inscritionsRelation, 1, disciplinesRelation, 0, pathjoinDisciplinesByStudent);
		join.open();
		relationInscritionsByDiscipline = join.getRelation();
		join.close();
		
		join = Join.getSelectedJoin(relationInscritionsByDiscipline, 0, studentsRelation, 0, pathjoinDisciplinesByStudent);
		join.open();
		relationInscritionsDisciplinesByStudent = join.getRelation(studentNameId);
		join.close();
				
		return relationInscritionsDisciplinesByStudent;
	}
	
	public Relation joinStudentsByCourse(){
		Relation relationStudentsByCourse;
		Join<String, Tuple> join;

		join = Join.getSelectedJoin(studentsRelation, 3, coursesRelation, 0, pathjoinStudentByCourse);
		join.open();
		
		relationStudentsByCourse = join.getRelation();
		
		join.close();
				
		return relationStudentsByCourse;
	}
	
	public Relation joinStudentsByDisciplnes(){
		Relation relationStudentsByDisciplines;
		Relation relationInscritionsByDisciplines;
		Join<String, Tuple> join;

		join = Join.getSelectedJoin(inscritionsRelation, 1, disciplinesRelation, 0, pathjoinStudentByDiscipline);
		join.open();
		relationInscritionsByDisciplines = join.getRelation();
		join.close();
		
		join = Join.getSelectedJoin(relationInscritionsByDisciplines, 0, studentsRelation, 0, pathjoinStudentByDiscipline);
		join.open();
		relationStudentsByDisciplines = join.getRelation();
		join.close();
				
		return relationStudentsByDisciplines;
	}

	
	public Relation joinInscritionsByCourse(){
		
		Relation relationInscritionsByStudent;
		Relation relationInscritionsStudentsByCourse;
		Join<String, Tuple> join;

		join = Join.getSelectedJoin(inscritionsRelation, 0, studentsRelation, 0, pathjoinInscritionsStudenByCourse);
		join.open();
		relationInscritionsByStudent = join.getRelation();
		join.close();
		
		join = Join.getSelectedJoin(relationInscritionsByStudent, 5, coursesRelation, 0, pathjoinInscritionsStudenByCourse);
		join.open();
		relationInscritionsStudentsByCourse = join.getRelation();
		join.close();
				
		return relationInscritionsStudentsByCourse;
	}

	public Relation joinDisciplinesByStudent(){
		
		Relation relationInscritionsByDiscipline;
		Relation relationInscritionsDisciplinesByStudent;
		Join<String, Tuple> join;
		
		
		join = Join.getSelectedJoin(inscritionsRelation, 1, disciplinesRelation, 0, pathjoinDisciplinesByStudent);
		join.open();
		relationInscritionsByDiscipline = join.getRelation();
		join.close();
		
		join = Join.getSelectedJoin(relationInscritionsByDiscipline, 0, studentsRelation, 0, pathjoinDisciplinesByStudent);
		join.open();
		relationInscritionsDisciplinesByStudent = join.getRelation();
		join.close();
				
		return relationInscritionsDisciplinesByStudent;
	}

	
	
	public void backupRelations() throws IOException{
		Date date = new Date();
		DateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm");
		
		backupStudentsRelation(dateFormat.format(date));
		backupCoursesRelation(dateFormat.format(date));
		backupDisciplinesRelation(dateFormat.format(date));
		backupInscritionsRelation(dateFormat.format(date));
	}

	private void backupStudentsRelation(String date) throws IOException{
		String path = "backups/[STUDENTS]_[" + date + "].zip";
		
		FileOperations.zipFiles(pathStudents, path);			
	}
	
	private void backupCoursesRelation(String date) throws IOException{
		String path = "backups/[COURSES]_[" + date + "].zip";
		
		FileOperations.zipFiles(pathCourses, path);			
	}
	
	private void backupDisciplinesRelation(String date) throws IOException{
		String path = "backups/[DISCIPLINES]_[" + date + "].zip";
		
		FileOperations.zipFiles(pathDisciplines, path);			
	}
	
	private void backupInscritionsRelation(String date) throws IOException{
		String path = "backups/[INSCRITIONS]_[" + date + "].zip";
		
		FileOperations.zipFiles(pathInscritions, path);				
	}	
}
